﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.ComponentModel;

namespace ObjectComposition
{
    public interface IProperty
    {
        void AddBinding(IBinding binding);
        void RemoveBinding(IBinding binding);
        void AddReevaluateBinding(IBinding binding);
        void RemoveReevaluateBinding(IBinding binding);
    }

    public class Property<Type> : IProperty
    {
        protected Type val;
        protected Type originalVal;

        [XmlIgnore]
        public bool IsInitializing { get; protected set; }

        [XmlIgnore]
        public Func<Type> Get { get; set; }

        protected List<IBinding> reevaluateBindings = new List<IBinding>();

        protected Action<Type> set;
        [XmlIgnore]
        public Action<Type> Set
        {
            get
            {
                return this.set;
            }
            set
            {
                this.IsInitializing = true;
                this.set = value;
                if(this.val != null && !this.val.Equals(default(Type)))
                    this.set(this.val);
                this.val = default(Type);
                this.IsInitializing = false;
            }
        }
        protected List<IBinding> bindings = new List<IBinding>();

		private bool editable = true;
        [XmlAttribute]
        [DefaultValue(true)]
        public bool Editable
		{
			get
			{
				return this.editable;
			}
			set
			{
				this.editable = value;
			}
		}

        public void AddReevaluateBinding(IBinding binding)
        {
            if (!this.reevaluateBindings.Contains(binding))
                this.reevaluateBindings.Add(binding);
        }

        public void RemoveReevaluateBinding(IBinding binding)
        {
            this.reevaluateBindings.Remove(binding);
        }

        public void AddBinding(IBinding binding)
        {
            if (!this.bindings.Contains(binding))
                this.bindings.Add(binding);
        }

        public void RemoveBinding(IBinding binding)
        {
            this.bindings.Remove(binding);
        }

        public void Changed()
        {
            foreach (IBinding b in this.bindings)
            {
                b.OnChanged(this);
            }
        }

        public Type Value
        {
            get
            {
                foreach (IBinding binding in this.reevaluateBindings)
                    binding.OnChanged(this);
                return this.Get != null ? this.Get() : this.val;
            }
            set
            {
                this.InternalSet(value, null);
            }
        }

        public void InternalSet(Type obj, IBinding binding)
        {
            if (this.Set != null)
                this.Set(obj);
            else
                this.val = obj;
            foreach (IBinding b in this.bindings)
            {
                if(b != binding)
                    b.OnChanged(this);
            }
        }
        
        public static implicit operator Type(Property<Type> obj)
        {
            return obj.Value;
        }

        public override string ToString()
        {
            Type obj = this.Value;
            return obj == null ? "" : obj.ToString();
        }
    }
}
